package fun.tan90.rt;

import fun.tan90.base.util.BaseUtil;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;

public class RouteTrainComputer {

    /**
     * 是否开始计算
     */
    private static final AtomicBoolean IS_COMPUTE = new AtomicBoolean(false);

    /**
     * 是否完成抢航判断
     */
    private static final AtomicBoolean IS_FINISH_QH = new AtomicBoolean(false);

    /**
     * 是否完成DTSL计算
     */
    private static final AtomicBoolean IS_FINISH_DTSL = new AtomicBoolean(false);

    /**
     * 当前是否越过起航线
     */
    private static final AtomicBoolean IS_YGQHX = new AtomicBoolean(false);

    /**
     * 是否完成绕标
     */
    private static final AtomicBoolean ROUND_MARK = new AtomicBoolean(false);
    /**
     * 是否前半程
     */
    private static final AtomicBoolean FIRST_HALF = new AtomicBoolean(false);
    /**
     * 当前圈数
     */
    private static final AtomicInteger CURRENT_LAP = new AtomicInteger(0);
    /**
     * 累计圈数
     */
    private static final AtomicInteger CUMULATIVE_LAPS = new AtomicInteger(0);


    private final int glDis;

    private double lat1;

    private double lon1;

    private double lat2;

    private double lon2;

    private double lat0;

    private double lon0;

    private double latBuoy;

    private double lonBuoy;

    private int buoyS0Dis;

    private int buoySide;

    private int roundMarkDis;

    private int trainingLaps;

    private RouteTrainData routeTrainData;

    private final BiConsumer<Date, RouteTrainData> routeTrainDataBiConsumer;


    /**
     * @param routeTrainDataBiConsumer 训练数据回调
     * @param glDis                    引航线判定距离 m
     * @param lat1                     Star1纬度
     * @param lon1                     Star1经度
     * @param lat2                     Star2纬度
     * @param lon2                     Star2经度
     * @param lat0                     起航线中点的纬度
     * @param lon0                     起航线中点的经度
     * @param latBuoy                  标1纬度
     * @param lonBuoy                  标1经度
     * @param buoyS0Dis                标位1->S0距离 m
     * @param buoySide                 标1位于线段AB的哪一侧 1（左侧）、-1（右侧）、0（线上）
     * @param roundMarkDis             绕标判断距离 m
     * @param trainingLaps             训练圈数
     */
    public RouteTrainComputer(BiConsumer<Date, RouteTrainData> routeTrainDataBiConsumer, int glDis,
                              double lat1, double lon1, double lat2, double lon2,
                              double lat0, double lon0, double latBuoy, double lonBuoy, int buoyS0Dis, int buoySide,
                              int roundMarkDis, int trainingLaps) {
        this.glDis = glDis;
        this.lat1 = lat1;
        this.lon1 = lon1;
        this.lat2 = lat2;
        this.lon2 = lon2;
        this.lat0 = lat0;
        this.lon0 = lon0;
        this.latBuoy = latBuoy;
        this.lonBuoy = lonBuoy;
        this.buoyS0Dis = buoyS0Dis;
        this.buoySide = buoySide;
        this.roundMarkDis = roundMarkDis;
        this.trainingLaps = trainingLaps;

        this.routeTrainData = new RouteTrainData();
        this.routeTrainDataBiConsumer = routeTrainDataBiConsumer;
    }

    /**
     * 计算
     *
     * @param trainId    本次训练ID
     * @param exeType    执行类型  10 开始计算是否抢航  20 开始计算训练数据
     * @param remainTime 剩余时长  单位秒
     * @param trainTime  训练时长  单位秒
     * @param latBoat    帆船纬度
     * @param lonBoat    帆船经度
     */
    public void compute(String trainId, String exeType, int remainTime, int trainTime, double latBoat, double lonBoat) {
        IS_COMPUTE.set(true);
        routeTrainData.setTrainId(trainId);
        routeTrainData.setLatitude(String.valueOf(latBoat));
        routeTrainData.setLongitude(String.valueOf(lonBoat));
        // 判断是否抢航
        if ("10".equals(exeType)) {
            if (!IS_FINISH_QH.get()) {
                int boatSide = BaseUtil.determineSide(lat1, lon1, lat2, lon2, latBoat, lonBoat);
                // 帆船与标1 首次同侧时
                if (boatSide == buoySide) {
                    if (IS_FINISH_QH.compareAndSet(false, true)) {
                        if (remainTime > 0) {
                            routeTrainData.setQh(true);
                            routeTrainData.setTtsl(-remainTime);
                        } else {
                            routeTrainData.setQh(false);
                            routeTrainData.setTtsl(trainTime);
                        }
                    }
                }
            }
            routeTrainDataBiConsumer.accept(new Date(), routeTrainData);
            return;
        }

        // 计算到达起航时间时，距起航线的距离 m
        if ("20".equals(exeType) && remainTime == 0 && IS_FINISH_DTSL.compareAndSet(false, true)) {
            int boatSide = BaseUtil.determineSide(lat1, lon1, lat2, lon2, latBoat, lonBoat);
            if (boatSide == 0) {
                routeTrainData.setDtsl(0.0D);
            } else {
                long s0BoatDistance = BaseUtil.calculateDistance(lat0, lon0, latBoat, lonBoat);
                Integer s01Bear = BaseUtil.calculateBear(lat0, lon0, lat1, lon1);
                Integer s0BoatBear = BaseUtil.calculateBear(lat0, lon0, latBoat, lonBoat);
                int sita = s01Bear - s0BoatBear;
                double dtsl = Math.abs(BigDecimal.valueOf(s0BoatDistance * Math.sin(Math.toRadians(sita))).setScale(1, RoundingMode.HALF_UP).doubleValue());
                routeTrainData.setDtsl(boatSide == buoySide ? -dtsl : dtsl);
            }
            routeTrainDataBiConsumer.accept(new Date(), routeTrainData);
            return;
        }

        // 计算训练数据
        if ("20".equals(exeType)) {
            if (IS_FINISH_QH.get() && IS_FINISH_DTSL.get()) {
                int boatSide = BaseUtil.determineSide(lat1, lon1, lat2, lon2, latBoat, lonBoat);
                double td = 0.0D;
                if (boatSide == buoySide) {
                    if (IS_YGQHX.compareAndSet(false, true)) {
                        // 进入前半程
                        FIRST_HALF.set(true);
                        // 当前圈数
                        CURRENT_LAP.incrementAndGet();
                    }
                    Integer s0BuoyBear = BaseUtil.calculateBear(lat0, lon0, latBuoy, lonBuoy);
                    Integer s0BoatBear = BaseUtil.calculateBear(lat0, lon0, latBoat, lonBoat);
                    int sita = s0BuoyBear - s0BoatBear;
                    double temp = Math.abs(BigDecimal.valueOf(buoyS0Dis * Math.cos(Math.toRadians(sita))).setScale(1, RoundingMode.HALF_UP).doubleValue());

                    if (buoyS0Dis - temp <= roundMarkDis) {
                        if (ROUND_MARK.compareAndSet(false, true)) {
                            // 进入后半程
                            FIRST_HALF.set(false);
                        }
                    } else {
                        ROUND_MARK.compareAndSet(true, false);
                    }

                    // 引航线逻辑
                    if (FIRST_HALF.get()) {
                        if (buoyS0Dis - temp <= glDis) {
                            routeTrainData.setGl(true);
                        }
                    } else {
                        routeTrainData.setGl(false);
                    }

                    // 实时航程
                    if (FIRST_HALF.get()) {
                        td = 2 * (CURRENT_LAP.get() - 1) * buoyS0Dis + temp;
                    } else {
                        td = 2 * (CURRENT_LAP.get() - 1) * buoyS0Dis + buoyS0Dis + (buoyS0Dis - temp);
                    }
                } else {
                    if (IS_YGQHX.compareAndSet(true, false)) {
                        // 累计圈数
                        CUMULATIVE_LAPS.incrementAndGet();
                        td = 2 * CUMULATIVE_LAPS.get() * buoyS0Dis;
                    }
                }
                td = BigDecimal.valueOf(td).setScale(1, RoundingMode.HALF_UP).doubleValue();
                // 航线训练总距离
                int l = buoyS0Dis * 2 * trainingLaps;
                double dtf = l - td;
                float cp = BigDecimal.valueOf(td / l * 100).setScale(1, RoundingMode.HALF_UP).floatValue();
                routeTrainData.setTd(td);
                routeTrainData.setL(l);
                routeTrainData.setDtf(dtf);
                routeTrainData.setCp(cp);
                routeTrainData.setCurLap(CURRENT_LAP.get());
                routeTrainData.setCumLaps(CUMULATIVE_LAPS.get());
                routeTrainData.setTrainLaps(trainingLaps);
                routeTrainDataBiConsumer.accept(new Date(), routeTrainData);
            }
        }
    }

    /**
     * PAD训练停止时 exeType==30时调用
     */
    public void reset() {
        IS_COMPUTE.set(false);

        IS_FINISH_QH.set(false);

        IS_YGQHX.set(false);

        IS_FINISH_DTSL.set(false);

        ROUND_MARK.set(false);
        FIRST_HALF.set(false);

        CURRENT_LAP.set(0);
        CUMULATIVE_LAPS.set(0);

        routeTrainData = new RouteTrainData();
    }

    /**
     * 更新标位参数
     *
     * @param lat1         Star1纬度
     * @param lon1         Star1经度
     * @param lat2         Star2纬度
     * @param lon2         Star2经度
     * @param lat0         起航线中点的纬度
     * @param lon0         起航线中点的经度
     * @param latBuoy      标1纬度
     * @param lonBuoy      标1经度
     * @param buoyS0Dis    标位1->S0距离 m
     * @param buoySide     标1位于线段AB的哪一侧 1（左侧）、-1（右侧）、0（线上）
     * @param roundMarkDis 绕标判断距离 m
     * @param trainingLaps 训练圈数
     */
    public void updateParam(double lat1, double lon1, double lat2, double lon2,
                            double lat0, double lon0, double latBuoy, double lonBuoy, int buoyS0Dis, int buoySide,
                            int roundMarkDis, int trainingLaps) {
        if (IS_COMPUTE.get()) {
            return;
        }
        this.lat1 = lat1;
        this.lon1 = lon1;
        this.lat2 = lat2;
        this.lon2 = lon2;
        this.lat0 = lat0;
        this.lon0 = lon0;
        this.latBuoy = latBuoy;
        this.lonBuoy = lonBuoy;
        this.buoyS0Dis = buoyS0Dis;
        this.buoySide = buoySide;
        this.roundMarkDis = roundMarkDis;
        this.trainingLaps = trainingLaps;
    }
}
